home *** CD-ROM | disk | FTP | other *** search
Oberon Text | 1991-02-15 | 15.7 KB | 416 lines | [.Ob./.Ob2] |
- Syntax10.Scn.Fnt
- Syntax10i.Scn.Fnt
- Syntax10b.Scn.Fnt
- MODULE EditKeys; (* CAS 2-Nov-90 *)
- (* Please Ed.Open EditKeys.Tool *)
- IMPORT
- Oberon, Input, Fonts, Display, Viewers, Texts, TextFrames, MenuViewers;
- CONST
- IdentLen = 32; DefaultFile = "EditKeys.Text"; DefaultMacro = "OTHERWISE";
- Menu = "System.Close System.Copy System.Grow Edit.Search Edit.Store";
- (*scanner symbols*)
- cmdSym = 0; nameSym = 1; stretchSym = 2; lparen = 3; rparen = 4; bslash = 5; eof = 6;
- (*built-in commands; first w/o, then w/ param*)
- writeCmd = "0"; charCmd = "1"; fntCmd = "2"; callCmd = "4";
- keepCmd = "5"; pickCmd = "6"; caretCmd = "7"; indentCmd = "8";
- (*preset.set*)
- fntPreset = 0; pickPreset = 1; caretPreset = 3;
- TYPE
- Ident = ARRAY IdentLen OF CHAR;
- Definition = POINTER TO DefinitionDesc;
- Sequence = POINTER TO SequenceDesc;
- DefinitionDesc = RECORD
- left, right: Definition;
- in: BOOLEAN;
- trig: Ident;
- seq: Sequence
- END;
- SequenceDesc = RECORD
- next: Sequence;
- sym: INTEGER;
- cmd: CHAR;
- def: Definition;
- stretch: Texts.Buffer
- END;
- defs: Definition;
- ch, cmd, hotKey: CHAR;
- sym, errs: INTEGER;
- errpos: LONGINT;
- name, trig: Ident;
- stretch, buf, indent: Texts.Buffer;
- T: Texts.Text;
- R: Texts.Reader;
- W, WB, WL: Texts.Writer; (*out, compose, Leda*)
- preset: RECORD
- set: SET;
- pos, caret: LONGINT;
- frame: TextFrames.Frame;
- def, fnt: Fonts.Font
- END;
- PROCEDURE Flip(VAR src, dst: ARRAY OF CHAR);
- VAR i, j: INTEGER;
- BEGIN i := -1; j := 0;
- REPEAT INC(i) UNTIL src[i] = 0X;
- WHILE i > 0 DO DEC(i); dst[j] := src[i]; INC(j) END;
- dst[j] := 0X
- END Flip;
- PROCEDURE WLog;
- BEGIN Texts.Append(Oberon.Log, W.buf)
- END WLog;
- PROCEDURE Ch(ch: CHAR);
- BEGIN Texts.Write(W, ch)
- END Ch;
- PROCEDURE Str(s: ARRAY OF CHAR);
- BEGIN Texts.WriteString(W, s)
- END Str;
- PROCEDURE FlipStr(s: ARRAY OF CHAR);
- VAR n: Ident;
- BEGIN Flip(s, n); Str(n)
- END FlipStr;
- PROCEDURE Gap;
- BEGIN Str(" ")
- END Gap;
- PROCEDURE Ln;
- BEGIN Texts.WriteLn(W)
- END Ln;
- PROCEDURE Char(ch: CHAR);
- VAR i, n: INTEGER; d: ARRAY 3 OF CHAR;
- BEGIN
- IF (ch < " ") OR (ch >= 7FX) THEN
- i := 0; n := ORD(ch); REPEAT d[i] := CHR(n MOD 10 + 30H); n := n DIV 10; INC(i) UNTIL n = 0;
- Ch("#"); WHILE i > 0 DO DEC(i); Ch(d[i]) END
- ELSE Ch(ch)
- END
- END Char;
- PROCEDURE Append(SB, DB: Texts.Buffer);
- BEGIN Texts.Copy(SB, DB); Texts.OpenBuf(SB)
- END Append;
- (* table handler *)
- PROCEDURE ResetDefs;
- BEGIN defs.right := NIL; defs.trig[0] := 0X; hotKey := "\"
- END ResetDefs;
- PROCEDURE Find(VAR name, trig: Ident; insert: BOOLEAN): Definition;
- VAR p, q, d, x: Definition; i, j: INTEGER;
- BEGIN Flip(name, trig); p := defs; d := p.right; q := NIL; x := NIL;
- WHILE d # NIL DO i := 0;
- WHILE (trig[i] # 0X) & (trig[i] = d.trig[i]) DO INC(i) END;
- IF trig[i] = d.trig[i] THEN x := d; d := NIL
- ELSIF trig[i] = 0X THEN q := d; d := NIL
- ELSIF trig[i] < d.trig[i] THEN p := d; d := d.left
- ELSE p := d; d := d.right
- END
- END;
- IF insert & (x = NIL) THEN NEW(x); x.right := q; x.in := FALSE; x.trig := trig;
- IF (q # NIL) & (q.left # NIL) & (q.left.trig < trig) THEN x.left := q.left; q.left := NIL ELSE x.left := NIL END;
- IF trig < p.trig THEN p.left := x ELSE p.right := x END
- END;
- RETURN x
- END Find;
- PROCEDURE Trigger(VAR trig: Ident): Definition;
- VAR d, x: Definition; i: INTEGER;
- BEGIN d := defs.right; x := NIL;
- WHILE d # NIL DO i := 0;
- WHILE (trig[i] # 0X) & (trig[i] = d.trig[i]) DO INC(i) END;
- IF trig[i] = d.trig[i] THEN RETURN d END;
- IF d.trig[i] = 0X THEN x := d END;
- IF trig[i] < d.trig[i] THEN d := d.left ELSE d := d.right END
- END;
- RETURN x
- END Trigger;
- (* macro compiler *)
- PROCEDURE Mark(err: ARRAY OF CHAR);
- BEGIN INC(errs);
- IF Texts.Pos(R) - errpos > 9 THEN
- errpos := Texts.Pos(R); Ln; Str(" pos "); Texts.WriteInt(W, errpos, 0); Gap; Str(err); WLog
- END
- END Mark;
- PROCEDURE GetCh;
- BEGIN Texts.Read(R, ch)
- END GetCh;
- PROCEDURE CharCode;
- VAR c: INTEGER;
- BEGIN c := 0;
- WHILE ("0" <= ch) & (ch <= "9") DO c := c * 10 + SHORT(ORD(ch) - 30H); GetCh END;
- name[0] := CHR(c); name[1] := 0X (*unchecked*)
- END CharCode;
- PROCEDURE Name;
- VAR i: INTEGER;
- BEGIN i := 0;
- REPEAT name[i] := ch; INC(i); GetCh
- UNTIL (ch <= " ") OR (ch = 22X) OR (ch = "#") OR (ch = "(") OR (ch = ")") OR (ch = "\") OR (ch = "^")
- OR R.eot OR (i = IdentLen-1);
- name[i] := 0X
- END Name;
- PROCEDURE Stretch;
- VAR beg, end: LONGINT;
- BEGIN beg := Texts.Pos(R); end := beg; GetCh;
- WHILE ~R.eot & (ch # 22X) DO INC(end); GetCh END;
- IF ch = 22X THEN GetCh;
- IF end > beg THEN NEW(stretch); Texts.OpenBuf(stretch); Texts.Save(T, beg, end, stretch)
- ELSE Mark("empty stretch")
- END
- ELSE Mark("Closing quote expected")
- END
- END Stretch;
- PROCEDURE Comment;
- BEGIN
- LOOP GetCh;
- IF ch = "(" THEN GetCh;
- IF ch = "*" THEN GetCh; Comment END
- ELSIF ch = "*" THEN GetCh;
- IF ch = ")" THEN GetCh; EXIT END
- END
- END
- END Comment;
- PROCEDURE GetSym;
- BEGIN sym := eof;
- REPEAT
- IF (0X <= ch) & (ch <= " ") THEN GetCh
- ELSIF ch = 22X THEN sym := stretchSym; Stretch
- ELSIF ch = "#" THEN sym := nameSym; GetCh; CharCode
- ELSIF ch = "(" THEN GetCh; IF ch = "*" THEN GetCh; Comment ELSE sym := lparen END
- ELSIF ch = ")" THEN sym := rparen; GetCh
- ELSIF ch = "\" THEN sym := bslash; GetCh
- ELSIF ch = "^" THEN sym := cmdSym; GetCh; cmd := ch; GetCh
- ELSE sym := nameSym; Name
- END
- UNTIL (sym # eof) OR R.eot
- END GetSym;
- PROCEDURE ParseText;
- VAR def: Definition; beg, seq: Sequence; f: BOOLEAN;
- BEGIN GetSym;
- IF sym = bslash THEN
- GetSym; IF sym = nameSym THEN hotKey := name[0]; GetSym ELSE Mark("hot-key code expected") END
- END;
- WHILE sym = nameSym DO GetSym; def := Find(name, trig, TRUE);
- IF sym = lparen THEN
- GetSym; NEW(beg); seq := beg; beg.next := NIL;
- WHILE sym IN {cmdSym, nameSym, stretchSym} DO
- NEW(seq.next); seq := seq.next; seq.sym := sym;
- IF sym = cmdSym THEN seq.cmd := cmd
- ELSIF sym = nameSym THEN seq.def := Find(name, trig, FALSE);
- IF seq.def = NIL THEN Mark("illegal forward reference") END
- ELSE (*sym = stretchSym*) seq.stretch := stretch
- END;
- GetSym
- END;
- def.seq := beg.next; IF sym = rparen THEN GetSym ELSE Mark(") expected") END
- ELSE Mark("( expected")
- END
- END;
- IF sym # eof THEN GetSym;
- IF sym # eof THEN Mark("unexpected trailing char.s") END
- END;
- stretch := NIL
- END ParseText;
- PROCEDURE ReadFile(name: ARRAY OF CHAR);
- BEGIN T := TextFrames.Text(name); Texts.OpenReader(R, T, 0); GetCh;
- IF ~R.eot THEN Str(" reading "); Str(name); WLog; errs := 0; errpos := -10; ParseText;
- IF errs = 0 THEN Str(" done") ELSE ResetDefs END;
- Ln; WLog
- END
- END ReadFile;
- (* macro processor *)
- PROCEDURE SetCaret(frame: TextFrames.Frame; pos: LONGINT);
- BEGIN
- IF frame.car > 0 THEN TextFrames.RemoveCaret(frame) END;
- TextFrames.SetCaret(frame, pos)
- END SetCaret;
- PROCEDURE Insert(frame: TextFrames.Frame; buf: Texts.Buffer);
- VAR pos, len: LONGINT;
- BEGIN pos := frame.carloc.pos; len := buf.len;
- Texts.Insert(frame.text, pos, buf); SetCaret(frame, pos + len)
- END Insert;
- PROCEDURE Delete(frame: TextFrames.Frame; len: LONGINT);
- VAR pos: LONGINT;
- BEGIN pos := frame.carloc.pos;
- Texts.Delete(frame.text, pos - len, pos); SetCaret(frame, pos - len)
- END Delete;
- PROCEDURE Err(def: Definition; s: ARRAY OF CHAR);
- BEGIN INC(errs); Gap; Str(s); Str(" in "); FlipStr(def.trig); Ln; WLog
- END Err;
- PROCEDURE Arg(def: Definition; class: SHORTINT; VAR S: Texts.Scanner; VAR stack: Sequence);
- VAR B: Texts.Buffer;
- BEGIN
- IF stack # NIL THEN T := TextFrames.Text(""); NEW(B); Texts.OpenBuf(B); Texts.Copy(stack.stretch, B);
- Texts.Append(T, B); Texts.OpenScanner(S, T, 0); Texts.Scan(S); stack := stack.next;
- IF S.class # class THEN Err(def, "illegal param type") END
- ELSE Err(def, "missing param")
- END
- END Arg;
- PROCEDURE Expand(def: Definition; VAR stack: Sequence);
- VAR seq, u: Sequence; S: Texts.Scanner; par: Oberon.ParList; res: INTEGER;
- BEGIN
- IF ~def.in THEN def.in := TRUE; seq := def.seq;
- WHILE seq # NIL DO
- IF seq.sym = cmdSym THEN
- IF seq.cmd = writeCmd THEN
- IF stack # NIL THEN Texts.Copy(stack.stretch, buf); stack := stack.next
- ELSE Err(def, "missing param")
- END
- ELSIF seq.cmd = charCmd THEN Arg(def, Texts.Int, S, stack);
- Texts.Write(WB, CHR(S.i)); Append(WB.buf, buf)
- ELSIF seq.cmd = fntCmd THEN Arg(def, Texts.Name, S, stack);
- INCL(preset.set, fntPreset); preset.fnt := Fonts.This(S.s)
- ELSIF seq.cmd = callCmd THEN Arg(def, Texts.Name, S, stack);
- IF errs = 0 THEN NEW(par); par.vwr := Oberon.FocusViewer; par.frame := par.vwr.dsc.next;
- par.text := T; par.pos := Texts.Pos(S); Oberon.Call(S.s, par, FALSE, res);
- IF res # 0 THEN Texts.WriteInt(W, res, 3); Err(def, "call error ") END
- END
- ELSIF seq.cmd = keepCmd THEN INCL(preset.set, fntPreset); preset.fnt := preset.def
- ELSIF seq.cmd = pickCmd THEN INCL(preset.set, pickPreset)
- ELSIF seq.cmd = caretCmd THEN INCL(preset.set, caretPreset); preset.caret := buf.len
- ELSIF seq.cmd = indentCmd THEN
- IF indent.len > 0 THEN Texts.Copy(indent, buf) END
- ELSE Err(def, "illegal built-in")
- END
- ELSIF seq.sym = nameSym THEN Expand(seq.def, stack)
- ELSE (*seq.sym = stretchSym*) NEW(u); u.next := stack; stack := u;
- NEW(u.stretch); Texts.OpenBuf(u.stretch); Texts.Copy(seq.stretch, u.stretch)
- END;
- seq := seq.next
- END;
- def.in := FALSE
- ELSE Err(def, "cyclic expansion")
- END
- END Expand;
- PROCEDURE Process(frame: TextFrames.Frame; ch: CHAR; VAR del: LONGINT);
- VAR def: Definition; pos, i: LONGINT; stack: Sequence; default: BOOLEAN;
- BEGIN errs := 0; del := 0; Texts.OpenBuf(buf); pos := frame.carloc.pos - (IdentLen-1);
- IF pos >= 0 THEN i := IdentLen-1 ELSE pos := 0; i := frame.carloc.pos END;
- trig[i] := 0X; Texts.OpenReader(R, frame.text, pos); REPEAT DEC(i); Texts.Read(R, trig[i]) UNTIL i = 0;
- def := Trigger(trig); default := def = NIL;
- IF default THEN name := DefaultMacro; def := Find(name, trig, FALSE) END;
- IF def # NIL THEN preset.set := {}; stack := NIL; errs := 0; Expand(def, stack);
- IF stack # NIL THEN Err(def, "superfluous param") END;
- IF errs = 0 THEN
- IF ~default THEN
- REPEAT INC(del) UNTIL def.trig[del] = 0X
- END;
- preset.frame := frame; preset.pos := frame.carloc.pos - del;
- IF caretPreset IN preset.set THEN INC(preset.pos, preset.caret) ELSE INC(preset.pos, buf.len) END
- END
- END
- END Process;
- (* editor interface *)
- PROCEDURE Key(frame: TextFrames.Frame; ch: CHAR; VAR handled: BOOLEAN);
- CONST BS = 1X; TAB = 9X; LF = 0AX; CR = 0DX; DEL = 7FX; RtArrow = 0C3X; LtArrow = 0C4X;
- VAR del, pos, beg: LONGINT; i: INTEGER; fnt: Fonts.Font; ch1: CHAR;
- BEGIN handled := TRUE; pos := frame.carloc.pos;
- IF pos > 0 THEN Texts.OpenReader(R, frame.text, pos - 1); Texts.Read(R, ch1); preset.def := R.fnt
- ELSE preset.def := Fonts.Default
- END;
- IF (preset.frame = frame) & (preset.pos = pos) & (fntPreset IN preset.set) THEN fnt := preset.fnt
- ELSE fnt := preset.def
- END;
- preset.set := {}; preset.frame := NIL;
- IF WL.fnt # fnt THEN Texts.SetFont(WL, fnt) END;
- IF ch = hotKey THEN beg := frame.carloc.org; Texts.OpenReader(R, frame.text, beg); Texts.Read(R, ch1);
- WHILE (Texts.Pos(R) <= pos) & (ch1 <= " ") DO Texts.Read(R, ch1) END;
- Texts.OpenBuf(indent); IF beg < Texts.Pos(R) - 1 THEN Texts.Save(frame.text, beg, Texts.Pos(R) - 1, indent) END;
- Process(frame, ch, del);
- IF errs = 0 THEN
- IF (frame.car > 0) & (frame.carloc.pos # pos) THEN preset.pos := frame.carloc.pos - del END;
- SetCaret(frame, pos);
- IF del > 0 THEN Delete(frame, del) END;
- IF pickPreset IN preset.set THEN T := TextFrames.Text(""); Texts.Append(T, buf);
- Texts.ChangeLooks(T, 0, T.len, {0}, preset.def, 0, 0); Texts.Save(T, 0, T.len, buf)
- END;
- Insert(frame, buf);
- IF (0 <= preset.pos) & (preset.pos <= frame.text.len) THEN SetCaret(frame, preset.pos) END
- END
- ELSIF (ch = BS) OR (ch = TAB) OR (ch = LF) OR (ch = CR) OR (ch = DEL) OR (ch = RtArrow) OR (ch = LtArrow) THEN
- handled := FALSE
- ELSE Texts.Write(WL, ch)
- END;
- IF (WL.buf.len > 0) & (frame.car > 0) THEN Insert(frame, WL.buf) END
- END Key;
- PROCEDURE* Handler(F: Display.Frame; VAR msg: Display.FrameMsg);
- CONST InvalMsg = -1;
- VAR frame: TextFrames.Frame; handled: BOOLEAN;
- BEGIN
- IF msg IS Oberon.InputMsg THEN
- WITH msg: Oberon.InputMsg DO frame := F(TextFrames.Frame);
- IF (msg.id = Oberon.consume) & (frame.car > 0) THEN Key(frame, msg.ch, handled);
- IF handled THEN msg.id := InvalMsg END
- ELSIF (msg.id = Oberon.track) & (msg.keys # {}) THEN preset.set := {}
- END
- END
- END
- END Handler;
- PROCEDURE GetHandler*;
- CONST Magic = -42;
- BEGIN
- IF Oberon.Par.pos = Magic THEN Oberon.Par.frame.handle := Handler END
- END GetHandler;
- (* commands *)
- PROCEDURE Reset*;
- BEGIN ResetDefs
- END Reset;
- PROCEDURE ReadFiles*;
- VAR S: Texts.Scanner;
- BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S);
- WHILE S.class = Texts.Name DO ReadFile(S.s); Texts.Scan(S) END;
- T := NIL
- END ReadFiles;
- PROCEDURE Definitions*;
- VAR
- S: Texts.Scanner; V: MenuViewers.Viewer; text: Texts.Text; tree: Definition;
- beg, end, time: LONGINT; x, y: INTEGER;
- PROCEDURE Sort(VAR d: Definition; VAR pat: ARRAY OF CHAR; VAR tree: Definition);
- VAR i: INTEGER; t: Definition; n: Ident;
- PROCEDURE Ins(VAR tree: Definition; t: Definition; VAR n: Ident);
- VAR m: Ident;
- BEGIN
- IF tree = NIL THEN tree := t
- ELSE Flip(tree.trig, m);
- IF n < m THEN Ins(tree.left, t, n) ELSE Ins(tree.right, t, n) END
- END
- END Ins;
- BEGIN
- IF d # NIL THEN Flip(d.trig, n); i := 0;
- WHILE (pat[i] # 0X) & (n[i] = pat[i]) DO INC(i) END;
- IF pat[i] = 0X THEN NEW(t); t^ := d^; t.left := NIL; t.right := NIL; Ins(tree, t, n) END;
- Sort(d.left, pat, tree); Sort(d.right, pat, tree)
- END
- END Sort;
- PROCEDURE Write(tree: Definition);
- PROCEDURE Seq(seq: Sequence);
- BEGIN
- WHILE seq # NIL DO
- IF seq.sym = cmdSym THEN Ch("^"); Ch(seq.cmd)
- ELSIF seq.sym = nameSym THEN
- IF (name[1] = 0X) & ((name[0] < " ") OR (name[0] > 7EX)) THEN Char(name[0])
- ELSE FlipStr(seq.def.trig)
- END
- ELSE (*seq.sym = stretchSym*) Ch(22X); Append(W.buf, buf); Texts.Copy(seq.stretch, buf); Ch(22X)
- END;
- seq := seq.next; IF seq # NIL THEN Ch(" ") END
- END
- END Seq;
- BEGIN
- IF tree # NIL THEN
- Write(tree.left); Gap; FlipStr(tree.trig); Gap; Ch("("); Seq(tree.seq); Ch(")"); Ln; Write(tree.right)
- END
- END Write;
- BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S);
- IF (S.line # 0) OR (S.class # Texts.String) & (S.class # Texts.Name) THEN
- Oberon.GetSelection(text, beg, end, time);
- IF time > 0 THEN Texts.OpenScanner(S, text, beg); Texts.Scan(S) END
- END;
- IF (S.line # 0) OR (S.class # Texts.String) & (S.class # Texts.Name) THEN S.s[0] := 0X END;
- Str("(hotkey is "); Char(hotKey); Ch(")"); Ln;
- tree := NIL; Sort(defs.right, S.s, tree); Texts.OpenBuf(buf); Write(tree); Append(W.buf, buf);
- Oberon.AllocateSystemViewer(Oberon.Mouse.X, x, y); text := TextFrames.Text(""); Texts.Append(text, buf);
- V := MenuViewers.New(TextFrames.NewMenu("EditKeys.Definitions", Menu),
- TextFrames.NewText(text, 0), TextFrames.menuH, x, y)
- END Definitions;
- PROCEDURE GetKeyCode*;
- BEGIN Str("EditKeys.KeyCode ('q' to quit)"); WLog;
- REPEAT Input.Read(ch); Ln; Gap; Char(ch); WLog UNTIL ch = "q";
- Ln; WLog
- END GetKeyCode;
- BEGIN Texts.OpenWriter(W); Texts.OpenWriter(WB); Texts.OpenWriter(WL); NEW(defs); NEW(buf); NEW(indent);
- ResetDefs; ReadFile(DefaultFile)
- END EditKeys.
-